'''
Created on Nov 24, 2012

@author: trananh
'''
from utils import utils
from Node import Node

class EntropyNode(Node):
    """
    Implements a simple binary node that uses the Entropy splitting strategy.
    
    Reference(s):
        - O'Hara, Stephen, and Bruce A. Draper. "Scalable action recognition
            with a subspace forest." Computer Vision and Pattern Recognition
            (CVPR), 2012 IEEE Conference on. IEEE, 2012.
    """

    # Algorithm parameters
    HistogramBins = 10                      # Number of bins to use
    EntropyThreshold = 1.79                 # Entropy splitting threshold (default 2.19)
    
    def checkSplittingCriteria(self):
        """
        Checks to see if the items within the node pass the splitting criteria.
            - Entropy Splitting: The node is split when the distribution of
                distances between the items in the node falls below some
                entropy threshold.
        """
        distances = [self.pivot.distance(x,featuresIdx=self.featuresIdx) for x in self.items]
        from numpy import histogram
        bins, _edges = histogram(distances, bins=EntropyNode.HistogramBins)
        ent = utils.entropy(list(bins))
        return ent < EntropyNode.EntropyThreshold
    
    def selectPivot(self):
        """
        Selects an item to be the pivot point.
            - Entropy Splitting: The pivot is randomly selected from the collection
                of items in the node.
        """
        from random import choice
        return choice(self.items)
    
    def selectThreshold(self):
        """
        Selects a threshold for splitting.
        Returns None if none could be found (e.g., all items yielding exactly the same
        distance from the pivot and cannot form 2 clusters, that is all items in
        this node are indistinguishable).
            - Entropy Splitting: The distances are divided into two clusters and the
                midpoint between the cluster centers is used as the threshold.
        """
        distances = [self.pivot.distance(x,featuresIdx=self.featuresIdx) for x in self.items]
        if len(set(distances)) <= 1:
            return None 
        from scipy.cluster.vq import kmeans2
        from numpy import asarray
        centroids, _ = kmeans2(asarray([distances]).transpose(), 2)
        return (centroids[0][0] + centroids[1][0]) / 2.0
